1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::{decode_to_string, encode_string};
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek, Write};
11
12#[derive(Debug)]
13pub struct EscudeBinListBuilder {}
15
16impl EscudeBinListBuilder {
17 pub const fn new() -> Self {
19 EscudeBinListBuilder {}
20 }
21}
22
23impl ScriptBuilder for EscudeBinListBuilder {
24 fn default_encoding(&self) -> Encoding {
25 Encoding::Cp932
26 }
27
28 fn build_script(
29 &self,
30 data: Vec<u8>,
31 filename: &str,
32 encoding: Encoding,
33 _archive_encoding: Encoding,
34 config: &ExtraConfig,
35 _archive: Option<&Box<dyn Script>>,
36 ) -> Result<Box<dyn Script>> {
37 Ok(Box::new(EscudeBinList::new(
38 data, filename, encoding, config,
39 )?))
40 }
41
42 fn extensions(&self) -> &'static [&'static str] {
43 &["bin"]
44 }
45
46 fn script_type(&self) -> &'static ScriptType {
47 &ScriptType::EscudeList
48 }
49
50 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
51 if buf_len > 4 && buf.starts_with(b"LIST") {
52 return Some(255);
53 }
54 None
55 }
56
57 fn can_create_file(&self) -> bool {
58 true
59 }
60
61 fn create_file<'a>(
62 &'a self,
63 filename: &'a str,
64 writer: Box<dyn WriteSeek + 'a>,
65 encoding: Encoding,
66 file_encoding: Encoding,
67 config: &ExtraConfig,
68 ) -> Result<()> {
69 create_file(
70 filename,
71 writer,
72 encoding,
73 file_encoding,
74 config.custom_yaml,
75 )
76 }
77}
78
79#[derive(Debug)]
80pub struct EscudeBinList {
82 pub entries: Vec<ListEntry>,
84 custom_yaml: bool,
85}
86
87impl EscudeBinList {
88 pub fn new(
95 data: Vec<u8>,
96 filename: &str,
97 encoding: Encoding,
98 config: &ExtraConfig,
99 ) -> Result<Self> {
100 let mut reader = MemReader::new(data);
101 let mut magic = [0; 4];
102 reader.read_exact(&mut magic)?;
103 if &magic != b"LIST" {
104 return Err(anyhow::anyhow!("Invalid Escude list file format"));
105 }
106 let wsize = reader.read_u32()?;
107 let mut entries = Vec::new();
108 loop {
109 let current = reader.stream_position()?;
110 if current as usize >= wsize as usize + 8 {
111 break;
112 }
113 let id = reader.read_u32()?;
114 let size = reader.read_u32()?;
115 let data = reader.read_exact_vec(size as usize)?;
116 entries.push(ListEntry {
117 id: id,
118 data: ListData::Unknown(data),
119 });
120 }
121 let mut s = EscudeBinList {
122 entries,
123 custom_yaml: config.custom_yaml,
124 };
125 match s.try_decode(filename, encoding) {
126 Ok(_) => {}
127 Err(e) => {
128 eprintln!("WARN: Failed to decode Escude list: {}", e);
129 crate::COUNTER.inc_warning();
130 }
131 }
132 Ok(s)
133 }
134
135 pub fn try_decode(&mut self, filename: &str, encoding: Encoding) -> Result<()> {
140 let filename = std::path::Path::new(filename);
141 if let Some(filename) = filename.file_name() {
142 let filename = filename.to_ascii_lowercase();
143 if filename == "enum_scr.bin" {
144 for ent in self.entries.iter_mut() {
145 let id = ent.id;
146 if let ListData::Unknown(unk) = &ent.data {
147 let mut reader = MemReader::new(unk.clone());
148 let element_size = if id == 0 {
149 132
150 } else if id == 1 {
151 100
152 } else if id == 2 {
153 36
154 } else if id == 3 {
155 104
156 } else if id == 9999 {
157 1
158 } else {
159 return Err(anyhow::anyhow!("Unknown enum source ID: {}", id));
160 };
161 let len = unk.len();
162 if len % element_size != 0 {
163 return Err(anyhow::anyhow!(
164 "Invalid enum source length: {} for ID: {}",
165 len,
166 id
167 ));
168 }
169 let count = len / element_size;
170 let data_entry = match id {
171 0 => ListData::Scr(EnumScr::Scripts(
172 reader.read_struct_vec::<ScriptT>(count, false, encoding)?,
173 )),
174 1 => ListData::Scr(EnumScr::Names(
175 reader.read_struct_vec::<NameT>(count, false, encoding)?,
176 )),
177 2 => ListData::Scr(EnumScr::Vars(
178 reader.read_struct_vec::<VarT>(count, false, encoding)?,
179 )),
180 3 => ListData::Scr(EnumScr::Scenes(
181 reader.read_struct_vec::<SceneT>(count, false, encoding)?,
182 )),
183 9999 => {
184 ListData::Unknown(unk.clone())
186 }
187 _ => return Err(anyhow::anyhow!("Unknown enum source ID: {}", id)),
188 };
189 ent.data = data_entry;
190 }
191 }
192 } else if filename == "enum_gfx.bin" {
193 for ent in self.entries.iter_mut() {
194 let id = ent.id;
195 if let ListData::Unknown(unk) = &ent.data {
196 let mut reader = MemReader::new(unk.clone());
197 let element_size = if id == 0 {
198 248
199 } else if id == 1 {
200 248
201 } else if id == 2 {
202 248
203 } else if id == 3 {
204 112
205 } else if id == 4 {
206 32
207 } else if id == 9999 {
208 1
209 } else {
210 return Err(anyhow::anyhow!("Unknown enum gfx ID: {}", id));
211 };
212 let len = unk.len();
213 if len % element_size != 0 {
214 return Err(anyhow::anyhow!(
215 "Invalid enum gfx length: {} for ID: {}",
216 len,
217 id
218 ));
219 }
220 let count = len / element_size;
221 let data_entry = match id {
222 0 => ListData::Gfx(EnumGfx::Bgs(
223 reader.read_struct_vec::<BgT>(count, false, encoding)?,
224 )),
225 1 => ListData::Gfx(EnumGfx::Evs(
226 reader.read_struct_vec::<EvT>(count, false, encoding)?,
227 )),
228 2 => ListData::Gfx(EnumGfx::Sts(
229 reader.read_struct_vec::<StT>(count, false, encoding)?,
230 )),
231 3 => ListData::Gfx(EnumGfx::Efxs(
232 reader.read_struct_vec::<EfxT>(count, false, encoding)?,
233 )),
234 4 => ListData::Gfx(EnumGfx::Locs(
235 reader.read_struct_vec::<LocT>(count, false, encoding)?,
236 )),
237 9999 => {
238 ListData::Unknown(unk.clone())
240 }
241 _ => return Err(anyhow::anyhow!("Unknown enum gfx ID: {}", id)),
242 };
243 ent.data = data_entry;
244 }
245 }
246 } else if filename == "enum_snd.bin" {
247 for ent in self.entries.iter_mut() {
248 let id = ent.id;
249 if let ListData::Unknown(unk) = &ent.data {
250 let mut reader = MemReader::new(unk.clone());
251 let element_size = if id == 0 {
252 196
253 } else if id == 1 {
254 128
255 } else if id == 2 {
256 128
257 } else if id == 3 {
258 128
259 } else if id == 9999 {
260 1
261 } else {
262 return Err(anyhow::anyhow!("Unknown enum sound ID: {}", id));
263 };
264 let len = unk.len();
265 if len % element_size != 0 {
266 return Err(anyhow::anyhow!(
267 "Invalid enum sound length: {} for ID: {}",
268 len,
269 id
270 ));
271 }
272 let count = len / element_size;
273 let data_entry = match id {
274 0 => ListData::Snd(EnumSnd::Bgm(
275 reader.read_struct_vec::<BgmT>(count, false, encoding)?,
276 )),
277 1 => ListData::Snd(EnumSnd::Amb(
278 reader.read_struct_vec::<AmbT>(count, false, encoding)?,
279 )),
280 2 => ListData::Snd(EnumSnd::Se(
281 reader.read_struct_vec::<SeT>(count, false, encoding)?,
282 )),
283 3 => ListData::Snd(EnumSnd::Sfx(
284 reader.read_struct_vec::<SfxT>(count, false, encoding)?,
285 )),
286 9999 => {
287 ListData::Unknown(unk.clone())
289 }
290 _ => return Err(anyhow::anyhow!("Unknown enum sound ID: {}", id)),
291 };
292 ent.data = data_entry;
293 }
294 }
295 }
296 }
297 Ok(())
298 }
299}
300
301fn create_file<'a>(
302 custom_filename: &'a str,
303 mut writer: Box<dyn WriteSeek + 'a>,
304 encoding: Encoding,
305 output_encoding: Encoding,
306 yaml: bool,
307) -> Result<()> {
308 let input = crate::utils::files::read_file(custom_filename)?;
309 let s = decode_to_string(output_encoding, &input, true)?;
310 let entries: Vec<ListEntry> = if yaml {
311 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
312 } else {
313 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
314 };
315 writer.write_all(b"LIST")?;
316 writer.write_u32(0)?; let mut total_size = 0;
318 for entry in entries {
319 let cur_pos = writer.stream_position()?;
320 writer.write_u32(entry.id)?;
321 writer.write_u32(0)?; entry.data.pack(&mut writer, false, encoding)?;
323 let end_pos = writer.stream_position()?;
324 let size = (end_pos - cur_pos - 8) as u32; writer.seek(std::io::SeekFrom::Start(cur_pos + 4))?; writer.write_u32(size)?;
327 writer.seek(std::io::SeekFrom::Start(end_pos))?; total_size += size + 8;
329 }
330 writer.seek(std::io::SeekFrom::Start(4))?; writer.write_u32(total_size)?;
332 writer.flush()?;
333 Ok(())
334}
335
336impl Script for EscudeBinList {
337 fn default_output_script_type(&self) -> OutputScriptType {
338 OutputScriptType::Custom
339 }
340
341 fn is_output_supported(&self, output: OutputScriptType) -> bool {
342 matches!(output, OutputScriptType::Custom)
343 }
344
345 fn default_format_type(&self) -> FormatOptions {
346 FormatOptions::None
347 }
348
349 fn custom_output_extension(&self) -> &'static str {
350 if self.custom_yaml { "yaml" } else { "json" }
351 }
352
353 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
354 let s = if self.custom_yaml {
355 serde_yaml_ng::to_string(&self.entries)
356 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
357 } else {
358 serde_json::to_string(&self.entries)
359 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
360 };
361 let mut writer = crate::utils::files::write_file(filename)?;
362 let s = encode_string(encoding, &s, false)?;
363 writer.write_all(&s)?;
364 writer.flush()?;
365 Ok(())
366 }
367
368 fn custom_import<'a>(
369 &'a self,
370 custom_filename: &'a str,
371 writer: Box<dyn WriteSeek + 'a>,
372 encoding: Encoding,
373 output_encoding: Encoding,
374 ) -> Result<()> {
375 create_file(
376 custom_filename,
377 writer,
378 encoding,
379 output_encoding,
380 self.custom_yaml,
381 )
382 }
383}
384
385#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
386pub struct ScriptT {
388 #[fstring = 64]
389 #[fstring_pad = 0x20]
390 pub file: String,
392 pub source: u32,
394 #[fstring = 64]
395 #[fstring_pad = 0x20]
396 pub title: String,
398}
399
400#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
401pub struct NameT {
403 #[fstring = 64]
404 #[fstring_pad = 0x20]
405 pub text: String,
407 pub color: u32,
409 #[fstring = 32]
410 #[fstring_pad = 0x20]
411 pub face: String,
413}
414
415#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
416pub struct VarT {
418 #[fstring = 32]
420 #[fstring_pad = 0x20]
421 pub name: String,
422 pub value: u16,
424 pub flag: u16,
426}
427
428#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
429pub struct SceneT {
431 pub script: u32,
433 #[fstring = 64]
435 #[fstring_pad = 0x20]
436 pub name: String,
437 #[fstring = 32]
439 #[fstring_pad = 0x20]
440 pub thumbnail: String,
441 pub order: i32,
443}
444
445#[derive(Debug, Serialize, Deserialize, StructPack)]
446#[serde(tag = "type", content = "data")]
447pub enum EnumScr {
449 Scripts(Vec<ScriptT>),
451 Names(Vec<NameT>),
453 Vars(Vec<VarT>),
455 Scenes(Vec<SceneT>),
457}
458
459#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
460pub struct BgT {
462 #[fstring = 32]
464 #[fstring_pad = 0x20]
465 name: String,
466 #[fstring = 64]
468 #[fstring_pad = 0x20]
469 file: String,
470 #[fstring = 128]
471 #[fstring_pad = 0x20]
472 option: String,
474 coverd: u32,
476 color: u32,
478 id: u32,
480 loc: u32,
482 order: i32,
484 link: u32,
486}
487
488#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
489pub struct EvT {
491 #[fstring = 32]
493 #[fstring_pad = 0x20]
494 name: String,
495 #[fstring = 64]
497 #[fstring_pad = 0x20]
498 file: String,
499 #[fstring = 128]
500 #[fstring_pad = 0x20]
501 option: String,
503 coverd: u32,
505 color: u32,
507 id: u32,
509 loc: u32,
511 order: i32,
513 link: u32,
515}
516
517#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
518pub struct StT {
520 #[fstring = 32]
521 #[fstring_pad = 0x20]
522 name: String,
524 #[fstring = 64]
525 #[fstring_pad = 0x20]
526 file: String,
528 #[fstring = 128]
529 #[fstring_pad = 0x20]
530 option: String,
532 coverd: u32,
534 color: u32,
536 id: u32,
538 loc: u32,
540 order: i32,
542 link: u32,
544}
545
546#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
547pub struct EfxT {
549 #[fstring = 32]
551 #[fstring_pad = 0x20]
552 name: String,
553 #[fstring = 64]
555 #[fstring_pad = 0x20]
556 file: String,
557 spot: i32,
559 dx: i32,
561 dy: i32,
563 r#loop: bool,
565 #[fvec = 3]
566 #[serde(skip, default = "exft_padding")]
567 padding: Vec<u8>,
569}
570
571fn exft_padding() -> Vec<u8> {
572 vec![0; 3]
573}
574
575#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
576pub struct Point {
578 x: i16,
580 y: i16,
582}
583
584#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
585pub struct LocT {
587 #[fvec = 8]
589 pt: Vec<Point>,
590}
591
592#[derive(Debug, Serialize, Deserialize, StructPack)]
593#[serde(tag = "type", content = "data")]
594pub enum EnumGfx {
596 Bgs(Vec<BgT>),
598 Evs(Vec<EvT>),
600 Sts(Vec<StT>),
602 Efxs(Vec<EfxT>),
604 Locs(Vec<LocT>),
606}
607
608#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
609pub struct BgmT {
611 #[fstring = 64]
612 #[fstring_pad = 0x20]
613 pub name: String,
615 #[fstring = 64]
616 #[fstring_pad = 0x20]
617 pub file: String,
619 #[fstring = 64]
620 #[fstring_pad = 0x20]
621 pub title: String,
623 pub order: i32,
625}
626
627#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
628pub struct AmbT {
630 #[fstring = 64]
631 #[fstring_pad = 0x20]
632 pub name: String,
634 #[fstring = 64]
635 #[fstring_pad = 0x20]
636 pub file: String,
638}
639
640#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
641pub struct SeT {
643 #[fstring = 64]
644 #[fstring_pad = 0x20]
645 pub name: String,
647 #[fstring = 64]
648 #[fstring_pad = 0x20]
649 pub file: String,
651}
652
653#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
654pub struct SfxT {
656 #[fstring = 64]
657 #[fstring_pad = 0x20]
658 pub name: String,
660 #[fstring = 64]
661 #[fstring_pad = 0x20]
662 pub file: String,
664}
665
666#[derive(Debug, Serialize, Deserialize, StructPack)]
667#[serde(tag = "type", content = "data")]
668pub enum EnumSnd {
670 Bgm(Vec<BgmT>),
672 Amb(Vec<AmbT>),
674 Se(Vec<SeT>),
676 Sfx(Vec<SfxT>),
678}
679
680#[derive(Debug, Serialize, Deserialize, StructPack)]
681#[serde(tag = "type", content = "data")]
682pub enum ListData {
684 Scr(EnumScr),
686 Gfx(EnumGfx),
688 Snd(EnumSnd),
690 Unknown(Vec<u8>),
692}
693
694#[derive(Debug, Serialize, Deserialize)]
695pub struct ListEntry {
697 id: u32,
698 pub data: ListData,
700}